<?php
/**
 * @name database: DB access library extension
 * @version 2.4.4, November 15, 2011
 * @copyright &copy; 2005 Novachok, A.
 * @author Snow (Novachok Alexandr) <novachok@gmail.net>
 * @package DB Access Library
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * PLACEHOLDERS:
 * %i - integer
 * %s - string, shoud be quoted
 * %f - float
 * %c - array Integer coma separate
 * %q - array quote coma separate
 *
 * TIPS:
 *
 * Insert named placeholders:
 * ->DB_QueryBuilder('SELECT * FROM t WHERE a=?vA b=?vB',
 * array("vA" => "cat's house", "vB" => "dog's house",));
 *
 */

class database
{
   var $link         = null;
   var $sql          = '';
   var $drop_values  = array();
   var $result       = array();
   var $rows         = 0;
   var $fetched      = array();
   var $error_status = FALSE;
   var $used_db      = '';

   var $supported_db = array('mysql');
   var $serial       = 20111115;
   var $version      = "2.4.4";
   var $mySqlVesion  = '';
   var $q_counter    = 0;

   static function getInstance()
   {
      static $object_store_singelton;
      if(!is_object($object_store_singelton) && func_num_args() > 3)
      {
         $dbhost = func_get_arg(0);
         $dbname = func_get_arg(1);
         $dbuser = func_get_arg(2);
         $dbpass = func_get_arg(3);
         $type   = func_num_args() == 5 ? func_get_arg(4) : 'mysql';
         $object_store_singelton = new database($dbhost, $dbname, $dbuser, $dbpass, $type);
      } elseif(!is_object($object_store_singelton))
      {
         # Try connect using Constants;
         $object_store_singelton = new database(DB_HOST, DB_NAME, DB_USER, DB_PASS);
      }
      return $object_store_singelton;
   }

   /**
    * database Constructor, Connect to DB and Select DateBase name
    *
    * @param string $dbhost
    * @param string $dbname
    * @param string $dbuser
    * @param string $dbpass
    */
   private function __construct($dbhost, $dbname, $dbuser='', $dbpass='', $type='mysql')
   {
      if(in_array($type, $this->supported_db) === false)
      {
         $this->used_db = 'mysql';
      } else
      {
         $this->used_db = $type;
      }

      $this->link = mysql_connect($dbhost, $dbuser, $dbpass) or die($this->error_handler('MySQL message: ' . mysql_error(), ''));
      mysql_select_db($dbname, $this->link) or die($this->error_handler('MySQL message: ' . mysql_error(), ''));

      $this->mySqlVesion = explode('.', mysql_get_server_info($this->link));
   }


   /**
    * database Destructor, Close DB connection
    */
   function __destruct()
   {
      $this->DB_close();
      //echo '<h1 style="color:#A00;">'. $this->q_counter .'</h1>';
   }

   /**
    * Make Query to DateBase
    */
   function DB_Query()
   {
      $this->result = null;
      $this->rows = 0;
      $this->fetched = null;

      if(!empty($this->sql))
      {
         $this->result = mysql_query($this->sql, $this->link) or die($this->error_handler(mysql_error(),$this->sql));
         $this->q_counter++;
         return $this->result;
      }
      return false;
   }

   /**
    * Fetch Results into two-dimensional Array
    *
    * @return array
    */
   function DB_Fetch()
   {
      $this->DB_Query();

      if($this->DB_GetRows() > 0)
      {
         for($i=0; $i<$this->rows; $i++)
         {
            $fetch[$i] = @mysql_fetch_assoc($this->result) or die($this->error_handler(mysql_error(),$this->sql));
         }
         $this->fetched = $fetch;
         return $this->fetched;
      }
      return false;
   }

   /**
    * Fetch Results of first Row into one-dimensional Array
    *
    * @return array
    */
   function DB_FetchFlat()
   {
      $FlatArray = false;
      $this->DB_Fetch();
      if($this->rows == 1)
      {
         $this->fetched = $this->fetched[0];
      }
      return $this->fetched;
   }

   /**
    * Get Row numbers of Result
    *
    * @return int
    */
   function DB_GetRows()
   {
      if(!empty($this->result))
      {
         $this->rows = mysql_num_rows($this->result);
         return $this->rows;
      }
      return false;
   }

   /**
    * Get Value of Given field id Given Row
    *
    * @param int $row
    * @param str $col
    * @return string
    */
   function DB_GetResult($row=0, $col=0)
   {
      if($this->DB_GetRows() > 0)
      {
         return @mysql_result($this->result, $row, $col);
      }
      return false;
   }

   /**
    * Get information about Affected rows
    *
    * @return integer
    */
   function DB_GetAffected()
   {
      return mysql_affected_rows($this->link);
   }

   /**
    * Get description of given table
    *
    * @param strint $table table name
    * @return array
    */
   function DB_TableInfo($table)
   {
      $this->DB_QueryBuilder('SHOW COLUMNS FROM %s', array($table));
      $tableInfo = $this->DB_Fetch();

      return $tableInfo;
   }

   /**
    * Gether information about last executed query;
    *
    * @return array
    */
   function DB_Info($param = '')
   {
      $query_info = false;
      $info = mysql_info($this->link);

      ereg("Records: ([0-9]*)", $info, $records);
      ereg("Duplicates: ([0-9]*)", $info, $dupes);
      ereg("Warnings: ([0-9]*)", $info, $warnings);
      ereg("Deleted: ([0-9]*)", $info, $deleted);
      ereg("Skipped: ([0-9]*)", $info, $skipped);
      ereg("Rows matched: ([0-9]*)", $info, $rows_matched);
      ereg("Changed: ([0-9]*)", $info, $changed);

      $query_info['records']    = $records[1];
      $query_info['duplicates'] = $dupes[1];
      $query_info['warnings']   = $warnings[1];
      $query_info['deleted']    = $deleted[1];
      $query_info['skipped']    = $skipped[1];
      $query_info['matched']    = $rows_matched[1];
      $query_info['changed']    = $changed[1];
      $query_info['mysql']      = $info;

      if($param != '' && isset($query_info[$param])) return $query_info[$param];
      return $query_info;
   }

   /**
    * Get the ID generated from the previous INSERT operation
    *
    * @return integer
    */
   function DB_LastID()
   {
      $this->sql = 'SELECT LAST_INSERT_ID()';
      $this->DB_Query();
      return $this->DB_GetResult();
   }

   /**
    * Get one level Array of Results by first column
    */
   function DB_GetFlat()
   {
      $this->DB_Query();

      if($this->DB_GetRows() > 0)
      {
         for($i=0; $i<$this->rows; $i++)
         {
            $fetch[$i] = $this->DB_GetResult($i, 0);
         }
         $this->fetched = $fetch;
         return $fetch;
      }
      return false;
   }


   /**
    *
    * @param string $field - Field name
    * @param string $table - Table name
    * @return <type>
    */
   function DB_GetNextID($field, $table, $condition = NULL)
   {
      if(!$field || !$table) return FALSE;
      $max = 1;
      $this->sql = 'SELECT MAX(' . $field . ') FROM ' . $table . (!empty($condition) ? ' WHERE '. $condition : '');
      $this->DB_Query();
      if($this->DB_GetResult())
      {
         $max = $this->DB_GetResult() + 1;
      }

      return $max;
   }


   /**
    * Start DB transaction
    * @return bool
    */
   function DB_StartTransaction()
   {
      $this->error_status = FALSE;
      if(join('', $this->mySqlVesion) > 503)
      {
         $this->sql = 'START TRANSACTION;';
         $this->DB_Query();
         return TRUE;
      }
      return FALSE;
   }


   /**
    * Finish DB transaction
    * @return bool
    */
   function DB_EndTransaction()
   {
      if(join('', $this->mySqlVesion) > 503)
      {
         if($this->error_status)
         {
            $this->sql = 'ROLLBACK';
            $this->DB_Query();
            $this->error_status = FALSE;
            return FALSE;
         } else
         {
            $this->sql = 'COMMIT';
            $this->DB_Query();
            return TRUE;
         }
      } elseif(!$this->error_status)
      {
         return TRUE;
      }

      return FALSE;
   }


   /**
    * Close connection to DB
    */
   function DB_close()
   {
      @mysql_close($this->link);
   }

   /**
    * Placeholder SECTION
    *
    * @param string $query
    * @param array $vars
    */
   function DB_QueryBuilder($query, $vars)
   {
      $this->sql = '';
      if(is_array($vars))
      {
         $vars = array_values($vars);
         $count_vars = count($vars);

         if($count_vars != preg_match_all('!%[sSiIfFcClLqQ]!', $query, $match))
         {
            return $this->error_handler('Unmatched number of vars and % placeholders',$query);
         }

         $vars_positions = array();
         $position = 0;

         for($i=0; $i<$count_vars; $i++)
         {
            $vars_positions[$i] = strpos($query, $match[0][$i], $position);
            $position = $vars_positions[$i] + 1;
         }

         $last_removed = null;
         $last_var = null;

         for($i = $count_vars - 1; $i >= 0; $i--)
         {
            if(isset($last_removed) && $last_removed < $vars_positions[$i])
            {
               continue;
            }

            $vars[$i] = $this->escaping_vars($vars[$i]);

            if(in_array($match[0][$i], array('%S','%I','%F','%C','%L','%Q')))
            {
               if(isset($last_var))
               {
                  $right_posit = strpos($query, ']', $last_var);
               } else
               {
                  $right_posit = strpos($query, ']', $vars_positions[$i]);
               }

               $str_slice = substr($query, 0, $vars_positions[$i]);
               $left_posit = strpos($str_slice, '[');

               if($right_posit === false || $left_posit === false)
               {
                  return $this->error_handler('Missing or Unmatched brackets',$query);
               }

               if(in_array($vars[$i], $this->drop_values, true))
               {
                  $last_removed = $left_posit;
                  $query = substr_replace($query, '', $left_posit, $right_posit-$left_posit+1);
                  $last_var = null;
               } elseif($i>0 && $vars_positions[$i-1] > $left_posit)
               {
                  $convert_var = $this->convert_var_types($vars[$i], $match[0][$i]);
                  $query = substr_replace($query, $convert_var, $vars_positions[$i], 2);
                  $last_var = $vars_positions[$i] + strlen($convert_var);
               } else
               {
                  $query = substr_replace($query, '', $right_posit, 1);
                  $query = substr_replace($query, $this->convert_var_types($vars[$i], $match[0][$i]), $vars_positions[$i], 2);
                  $query = substr_replace($query, '', $left_posit, 1);
                  $last_var = null;
               }
            } else
            {
               $query = substr_replace($query, $this->convert_var_types($vars[$i], $match[0][$i]), $vars_positions[$i], 2);
            }
         }
      }

      $this->sql = $query;
      return $this->sql;
   }

   /**
    * Escape function
    *
    * @param string|array $var
    * @return string
    */
   function escaping_vars($var)
   {
      if(is_array($var))
      {
         foreach($var as $value)
         {
            $esvar[] = $this->escaping_vars($value);
         }
         return $esvar;
      }

      if(function_exists("mysql_real_escape_string") && $this->used_db != 'flatfile')
      {
         if(!isset($this->link))
         {
            return mysql_real_escape_string(stripslashes($var));
         } else
         {
            return mysql_real_escape_string(stripslashes($var), $this->link);
         }
      } elseif(function_exists("mysql_escape_string"))
      {
         return mysql_escape_string(stripslashes($var));
      } else
      {
         return addslashes($var);
      }
      break;
   }

   function error_handler($err, $query)
   {
      $this->error_status = TRUE;
      $ErrorHandler = error::getInstance();
      $ErrorHandler->setError(array(
         'message' => '<b>Database ERROR MESSAGE: </b>' . $err . '<br><b>QUERY: </b>' . $query . '<br><br>'
         ), 3);

      return FALSE;
   }

   /**
    * Convert Variable type into...
    *
    * @param var $var
    * @param string $type
    * @return var
    */
   function convert_var_types($var, $type)
   {
      switch ($type)
      {
         case '%i':	// Integer
         case '%I':
            settype($var, 'integer');
            break;

         case '%f':	// Float
         case '%F':
            settype($var, 'float');
            break;

         case '%c':	// Array Integer coma separate
         case '%C':
            settype($var, 'array');
            for($i=0, $x=count($var); $i<$x; $i++)
            {
               settype($var[$i], 'integer');
            }
            $var = implode(',', $var);
            if($var == '0')
            {
               $var = '0';
            }
            break;

         case '%l':	// Array coma separate
         case '%L':
            settype($var, 'array');
            $var = implode(',', $var);
            break;

         case '%q':	// Array quote coma separate
         case '%Q':
            settype($var, 'array');
            $var = "'".implode("','", $var)."'";
            break;
      }

      return $var;
   }
}
